﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

using Storm.TextEditor.Core;
using Storm.TextEditor.Core.Splitting;
using Storm.TextEditor.Drawing;
using Storm.TextEditor.Editor.CodeCompletion;
using Storm.TextEditor.Editor.Forms;
using Storm.TextEditor.Editor.Interacting;
using Storm.TextEditor.Editor.Text;
using Storm.TextEditor.Editor.Undo;
using Storm.TextEditor.Interacting;
using Storm.TextEditor.Interacting.Keyboard;
using Storm.TextEditor.Interacting.Scrolling;
using Storm.TextEditor.Parser.Objects;
using Storm.TextEditor.Parser.Objects.Collections;
using Storm.TextEditor.Parser.XML;
using Storm.TextEditor.Win32;
using Storm.TextEditor.Win32.Enums;

using ScrollEventArgs = Storm.TextEditor.Interacting.Scrolling.ScrollEventArgs;
using ScrollEventHandler = Storm.TextEditor.Interacting.Scrolling.ScrollEventHandler;

namespace Storm.TextEditor.Editor
{
	/// <summary>
	/// Represents the TextEditor's child control that takes care of all the actual input from the user.
	/// </summary>
	[ToolboxItem(false)]
	public sealed class TextEditorBase
		: SplitViewChildControl
	{
		#region Fields

		private int mouseX = 0;
		private int mouseY = 0;

		private int maxCharWidth	= 8;
		private int tabSpaces		= 4;

		private double scrollHandlerCurrentPosition = 0;
		private string currentFileName				= null;

		private bool keepTabs		= false;
		private bool overWrite		= false;
		private bool keyDownHandled = false;

		private Selection	selection	= null;
		private Caret		caret		= null;

		private MouseButtons mouseButton	= MouseButtons.None;
		private TextDrawType textDrawType	= TextDrawType.StarBorder;

		private CodeCompletionWindow codeCompletionWindow = null;

		private WeakTimer		caretTimer			= null;
		private FindReplace		findReplaceDialog	= null;
		private ScrollHandler	scrollHandler		= null;

		private PictureBox	filler	= null;
		private ToolTip		tooltip = null;

		private IMEWindow imeWindow	= null;

		private IContainer components;

		private IPainter  painter   = null;
		private ViewPoint viewPoint = new ViewPoint();

		#region Events

		/// <summary>
		/// Occurs when the clipboard is updated.
		/// </summary>
		public event CopyHandler ClipboardUpdated = null;

		/// <summary>
		/// Occurs when the caret has changed.
		/// </summary>
		public event EventHandler CaretChange = null;

		/// <summary>
		/// Occurs when the selection has changed.
		/// </summary>
		public event EventHandler SelectionChange = null;

		/// <summary>
		/// Occurs when the mouse pointer is over a row and a mouse button is pressed.
		/// </summary>
		public event RowMouseHandler RowMouseDown = null;

		/// <summary>
		/// Occurs when the mouse pointer is moved over a row.
		/// </summary>
		public event RowMouseHandler RowMouseMove = null;

		/// <summary>
		/// Occurs when a mouse button has been released.
		/// </summary>
		public event RowMouseHandler RowMouseUp = null;

		/// <summary>
		/// Occurs when a row has been clicked.
		/// </summary>
		public event RowMouseHandler RowClick = null;

		/// <summary>
		/// Occurs when when a row has been double clicked.
		/// </summary>
		public event RowMouseHandler RowDoubleClick = null;

		/// <summary>
		/// Occurs when a key is pressed while the control has focus.
		/// </summary>
		public new event KeyEventHandler KeyDown = null;

		#endregion

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the painter of the TextEditorBase.
		/// </summary>
		public IPainter Painter
		{
			get { return painter; }
			set { painter = value; }
		}

		/// <summary>
		/// Gets or sets the view point of the TextEditorBase.
		/// </summary>
		public ViewPoint ViewPoint
		{
			get { return viewPoint; }
			set { viewPoint = value; }
		}

		/// <summary>
		/// Gets or sets whether to keep tabs when indenting or replace them with spaces.
		/// </summary>
		public bool KeepTabs
		{
			get { return keepTabs; }
			set { keepTabs = value; }
		}

		/// <summary>
		/// Gets or sets the number of spaces a tab contains.
		/// </summary>
		public int TabSpaces
		{
			get { return TextEditor.TabSpaces; }
			set { TextEditor.TabSpaces = value; }
		}

		/// <summary>
		/// Gets or sets the FindReplace dialog of the TextEditorBase.
		/// </summary>
		public FindReplace FindReplaceDialog
		{
			get
			{
				this.CreateFindForm();
				return findReplaceDialog;
			}
			set { findReplaceDialog = value; }
		}

		/// <summary>
		/// Gets or sets the IMEWindow of the TextEditorBase.
		/// </summary>
		public IMEWindow IMEWindow
		{
			get { return imeWindow; }
			set { imeWindow = value; }
		}

		/// <summary>
		/// Gets or sets the current filename.
		/// </summary>
		[Browsable(false)]
		[ReadOnly(true)]
		public string FileName
		{
			get { return currentFileName; }
			set
			{
				if (currentFileName != value)
					currentFileName = value;
			}
		}

		/// <summary>
		/// Gets or sets the size of tabs in pixels.
		/// </summary>
		public int PixelTabSize
		{
			get { return this.TextEditor.TabSpaces * this.ViewPoint.CharWidth; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase is in Overwrite mode.
		/// </summary>
		[Browsable(false)]
		public bool OverWrite
		{
			get { return overWrite; }
		}

		/// <summary>
		/// Gets whether the user would be able to do a copy action.
		/// </summary>
		[Browsable(false)]
		public bool CanCopy
		{
			get { return this.Selection.IsValid; }
		}

		/// <summary>
		/// Gets whether the user would be able to do a paste action.
		/// </summary>
		[Browsable(false)]
		public bool CanPaste
		{
			get
			{
				string clipboardData = "";

				try
				{
					IDataObject dataObject = Clipboard.GetDataObject();

					if (dataObject.GetDataPresent(DataFormats.Text))
						clipboardData = dataObject.GetData(DataFormats.Text) as string;

					if (clipboardData != null)
						return true;
				}
				catch
				{
				}

				return false;
			}
		}

		/// <summary>
		/// Gets whether the user would be able to do an undo action.
		/// </summary>
		[Browsable(false)]
		public bool CanUndo
		{
			get { return this.Document.UndoStep > 0; }
		}

		/// <summary>
		/// Gets whether the user would be able to do a redo action.
		/// </summary>
		[Browsable(false)]
		public bool CanRedo
		{
			get
			{
				if (this.Document.UndoStep >= this.Document.UndoBuffer.Count)
					return false;
				else
					return true;
			}
		}

		/// <summary>
		/// Gets the size (in pixels) of the font to use when rendering the content.
		/// </summary>
		public float FontSize
		{
			get { return this.TextEditor.FontSize; }
		}

		/// <summary>
		/// Gets the indention style to use when inserting new lines.
		/// </summary>
		public IndentStyle Indent
		{
			get { return this.TextEditor.Indent; }
		}

		/// <summary>
		/// Gets the SyntaxDocument the TextEditorBase is currently attatched to.
		/// </summary>
		public SyntaxDocument Document
		{
			get { return this.TextEditor.Document; }
		}

		/// <summary>
		/// Gets the delay in milliseconds before the tooltip is displayed when hovering a collapsed section.
		/// </summary>
		public int TooltipDelay
		{
			get { return this.TextEditor.TooltipDelay; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase is readonly.
		/// </summary>
		public bool ReadOnly
		{
			get { return this.TextEditor.ReadOnly; }
		}

		/// <summary>
		/// Gets or sets the name of the font to use when rendering the TextEditorBase.
		/// </summary>
		public string FontName
		{
			get { return this.TextEditor.FontName; }
		}

		/// <summary>
		/// Gets the style to use when painting with Alt + (Arrow) keys.
		/// </summary>
		public TextDrawType TextDrawStyle
		{
			get { return textDrawType; }
			set { textDrawType = value; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should render bracket matching.
		/// </summary>
		public bool BracketMatching
		{
			get { return this.TextEditor.BracketMatching; }
		}

		/// <summary>
		/// Gets whether the matching bracket should become bold when matched.
		/// </summary>
		public bool BracketBold
		{
			get { return this.TextEditor.BracketBold; }
		}

		/// <summary>
		/// Gets whether the matching bracket should become italic when matched.
		/// </summary>
		public bool BracketItalic
		{
			get { return this.TextEditor.BracketItalic; }
		}

		/// <summary>
		/// Gets whether the matching bracket should become underlined when matched.
		/// </summary>
		public bool BracketUnderline
		{
			get { return this.TextEditor.BracketUnderline; }
		}

		/// <summary>
		/// Gets whether the matching bracket should become strikethroughed when matched.
		/// </summary>
		public bool BracketStrikethrough
		{
			get { return this.TextEditor.BracketStrikethrough; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should render whitespace chars.
		/// </summary>
		public bool VirtualWhitespace
		{
			get { return this.TextEditor.VirtualWhitespace; }
		}

		/// <summary>
		/// Gets the Color of the horizontal separators (a'la Visual Basic 6).
		/// </summary>
		public Color SeparatorColor
		{
			get { return this.TextEditor.SeparatorColor; }
		}

		/// <summary>
		/// Gets the text color to use when rendering bracket matching.
		/// </summary>
		public Color BracketForeColor
		{
			get { return this.TextEditor.BracketForeColor; }
		}

		/// <summary>
		/// Gets or sets the border color of active selection areas.
		/// </summary>
		public Color SelectionBorderColor
		{
			get { return this.TextEditor.SelectionBorderColor; }
		}

		/// <summary>
		/// Gets or sets the border color of inactive selection areas.
		/// </summary>
		public Color InactiveSelectionBorderColor
		{
			get { return this.TextEditor.InactiveSelectionBorderColor; }
		}

		/// <summary>
		/// Gets the background color to use when rendering bracket matches.
		/// </summary>
		public Color BracketBackColor
		{
			get { return this.TextEditor.BracketBackColor; }
		}

		/// <summary>
		/// Gets the background color to use when rendering selected text.
		/// </summary>
		public Color SelectionBackColor
		{
			get { return this.TextEditor.SelectionBackColor; }
		}

		/// <summary>
		/// Gets the background color to use when rendering inactive selected text.
		/// </summary>
		public Color InactiveSelectionBackColor
		{
			get { return this.TextEditor.InactiveSelectionBackColor; }
		}

		/// <summary>
		/// Gets the color of the border between the line number area and the folding area.
		/// </summary>
		public Color LineNumberBorderColor
		{
			get { return this.TextEditor.LineNumberBorderColor; }
		}

		/// <summary>
		/// Gets the text color to use when rendering breakpoints.
		/// </summary>
		public Color BreakpointForeColor
		{
			get { return this.TextEditor.BreakpointForeColor; }
		}

		/// <summary>
		/// Gets the back color to use when rendering breakpoints.
		/// </summary>
		public Color BreakpointBackColor
		{
			get { return this.TextEditor.BreakpointBackColor; }
		}

		/// <summary>
		/// Gets the text color to use when rendering line numbers.
		/// </summary>
		public Color LineNumberForeColor
		{
			get { return this.TextEditor.LineNumberForeColor; }
		}

		/// <summary>
		/// Gets the back color to use when rendering the line number area.
		/// </summary>
		public Color LineNumberBackColor
		{
			get { return this.TextEditor.LineNumberBackColor; }
		}

		/// <summary>
		/// Gets the color of the gutter margin.
		/// </summary>
		public Color GutterMarginColor
		{
			get { return this.TextEditor.GutterMarginColor; }
		}

		/// <summary>
		/// <summary>
		/// Gets or sets the color to use when rendering an expansion's background color.
		/// </summary>
		public Color ExpansionBackgroundColor
		{
			get { return this.TextEditor.ExpansionBackgroundColor; }
		}

		/// <summary>
		/// Gets or sets the background color of the client area.
		/// </summary>
		public override Color BackColor
		{
			get { return this.TextEditor.BackColor; }
			set { this.TextEditor.BackColor = value; }
		}

		/// <summary>
		/// Gets the background color to use when rendering the active line.
		/// </summary>
		public Color HighlightedLineColor
		{
			get { return this.TextEditor.HighlightedLineColor; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should highlight the active line.
		/// </summary>
		public bool HighlightActiveLine
		{
			get { return this.TextEditor.HighlightActiveLine; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should render whitespace chars.
		/// </summary>
		public bool ShowWhitespace
		{
			get { return this.TextEditor.ShowWhitespace; }
		}

		/// <summary>
		/// Gets whether the line number margin should be visible or not.
		/// </summary>
		public bool ShowLineNumbers
		{
			get { return this.TextEditor.ShowLineNumbers; }
		}

		/// <summary>
		/// Gets whether the gutter margin should be visible or not.
		/// </summary>
		public bool ShowGutterMargin
		{
			get { return this.TextEditor.ShowGutterMargin; }
		}

		/// <summary>
		/// Gets the width of the gutter margin. (In pixels)
		/// </summary>
		public int GutterMarginWidth
		{
			get { return this.TextEditor.GutterMarginWidth; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should render Tab Guides.
		/// </summary>
		public bool ShowTabGuides
		{
			get { return this.TextEditor.ShowTabGuides; }
		}

		/// <summary>
		/// Gets the color to use when rendering whitespace chars.
		/// </summary>
		public Color WhitespaceColor
		{
			get { return this.TextEditor.WhitespaceColor; }
		}

		/// <summary>
		/// Gets the color to use when rendering tab guides.
		/// </summary>
		public Color TabGuideColor
		{
			get { return this.TextEditor.TabGuideColor; }
		}

		/// <summary>
		/// Gets the color to use when rendering bracket matching borders.
		/// </summary>
		public Color BracketBorderColor
		{
			get { return this.TextEditor.BracketBorderColor; }
		}

		/// <summary>
		/// Gets the color to use when rendering a hovered row.
		/// </summary>
		public Color RowHoverBackColor
		{
			get { return this.TextEditor.RowHoverBackColor; }
		}

		/// <summary>
		/// Gets or sets the color to use when rendering a row's expansion's inner symbol. (-/+)
		/// </summary>
		public Color ExpansionSymbolColor
		{
			get { return this.TextEditor.ExpansionSymbolColor; }
		}

		/// <summary>
		/// Gets the color to use when rendering Outline symbols.
		/// </summary>
		public Color OutlineColor
		{
			get { return this.TextEditor.OutlineColor; }
		}

		/// <summary>
		/// Gets the color to use when rendering a collapsed expansion's background.
		/// </summary>
		public Color CollapsedBackgroundColor
		{
			get { return this.TextEditor.CollapsedBackgroundColor; }
		}

		/// <summary>
		/// Gets or sets whether the TextEditorBase should use smooth scrolling.
		/// </summary>
		public bool SmoothScroll
		{
			get { return this.TextEditor.SmoothScroll; }
		}

		/// <summary>
		/// Gets the number of pixels the screen should be scrolled per scroll when smooth scrolling is enabled.
		/// </summary>
		public int SmoothScrollSpeed
		{
			get { return this.TextEditor.SmoothScrollSpeed; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should parse all of its contents when more content is drag dropped/pasted into it.
		/// </summary>
		public bool ParseOnPaste
		{
			get { return this.TextEditor.ParseOnPaste; }
		}

		/// <summary>
		/// Gets a value indicating whether the TextEditorBase should use a dotted margin border.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if the TextEditorBase should use a dotted margin border; otherwise, <c>false</c>.
		/// </value>
		public bool UseDottedMarginBorder
		{
			get { return this.TextEditor.UseDottedMarginBorder; }
		}

		/// <summary>
		/// Gets the Caret object.
		/// </summary>
		public Caret Caret
		{
			get { return caret; }
		}

		/// <summary>
		/// Gets the Selection object.
		/// </summary>
		public Selection Selection
		{
			get { return selection; }
		}

		/// <summary>
		/// Gets the code completion window.
		/// </summary>
		/// <value>The code completion window.</value>
		public CodeCompletionWindow CodeCompletionWindow
		{
			get { return codeCompletionWindow; }
		}

		#endregion

		#region Methods

		#region Private

		/// <summary>
		/// Finds the matched word.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="wordToSearch">The word to search.</param>
		/// <returns>The found word.</returns>
		private string FindMatchedWord(ArrayList list, string wordToSearch)
		{
			int binaryMatch = 0;

			string nextMatch = "";
			string lastMatch = "";

			ArrayList nextList = null;
			list.Sort();

			binaryMatch = list.BinarySearch(wordToSearch);
			if (binaryMatch < 0)
				binaryMatch = ~binaryMatch;

			if (binaryMatch > 0)
			{
				try
				{
					if (binaryMatch < list.Count)
						nextList = list.GetRange(binaryMatch + 1, 1);
				}
				catch
				{
					nextList = list.GetRange(binaryMatch, 1);
					lastMatch = nextList[0].ToString();
				}
			}

			if (nextList != null)
				nextMatch = nextList[0].ToString();

			if (nextMatch.IndexOf(wordToSearch, StringComparison.OrdinalIgnoreCase) < 0)
			{
				if (binaryMatch < list.Count)
					lastMatch = list.GetRange(binaryMatch, 1)[0].ToString();
			}

			return lastMatch;
		}

		/// <summary>
		/// Creates the find form.
		/// </summary>
		private void CreateFindForm()
		{
			if (this.TextEditor.DisableFindForm == false && findReplaceDialog == null)
				this.FindReplaceDialog = new FindReplace(this);
		}

		/// <summary>
		/// Updates the sizes and visibility of the SplitViewChildControl's child Controls,
		/// including the left and top SplitViewThumbControl and the ScrollBars. Also updates margins.
		/// </summary>
		private void UpdateContent()
		{
			this.SuspendLayout();
			if (this.Visible == true && this.Width > 0 && this.Height > 0 && this.IsHandleCreated == true)
			{
				if (filler == null)
					return;

				this.TopThumb.Width = SystemInformation.VerticalScrollBarWidth;
				this.LeftThumb.Height = SystemInformation.HorizontalScrollBarHeight;

				vScroll.Width = SystemInformation.VerticalScrollBarWidth;
				hScroll.Height = SystemInformation.HorizontalScrollBarHeight;

				if (this.TopThumbVisible == true)
				{
					vScroll.Top = this.TopThumb.Height;
					if (hScroll.Visible == true)
						vScroll.Height = this.ClientHeight - hScroll.Height - this.TopThumb.Height;
					else
						vScroll.Height = this.ClientHeight - this.TopThumb.Height;
				}
				else
				{
					if (hScroll.Visible == true)
						vScroll.Height = this.ClientHeight - hScroll.Height;
					else
						vScroll.Height = this.ClientHeight;

					vScroll.Top = 0;
				}

				if (this.LeftThumbVisible == true)
				{
					hScroll.Left = this.LeftThumb.Width;
					if (vScroll.Visible == true)
						hScroll.Width = this.ClientWidth - vScroll.Width - this.LeftThumb.Width;
					else
						hScroll.Width = this.ClientWidth - this.LeftThumb.Width;
				}
				else
				{
					if (vScroll.Visible == true)
						hScroll.Width = this.ClientWidth - vScroll.Width;
					else
						hScroll.Width = this.ClientWidth;

					hScroll.Left = 0;
				}

				if (this.Painter != null)
					this.Painter.Resize();

				vScroll.Left = this.ClientWidth - vScroll.Width;
				hScroll.Top = this.ClientHeight - hScroll.Height;

				this.LeftThumb.Left = 0;
				this.LeftThumb.Top = hScroll.Top;

				this.TopThumb.Left = vScroll.Left;
				this.TopThumb.Top = 0;

				filler.Left = vScroll.Left;
				filler.Top = hScroll.Top;
				filler.Width = vScroll.Width;
				filler.Height = hScroll.Height;
			}

			this.ResumeLayout(false);
		}

		/// <summary>
		/// Inserts a new line.
		/// </summary>
		private void InsertEnter()
		{
			this.Caret.CropPosition();
			if (this.Selection.IsValid == true)
			{
				this.Selection.DeleteSelection();
				this.InsertEnter();
			}
			else
			{
				Row currentRow = this.Caret.CurrentRow;
				string indentationLevel = "";
				switch (this.Indent)
				{
					case IndentStyle.None:
						this.Document.InsertText("\n", this.Caret.Position.X, this.Caret.Position.Y);

						this.Caret.CurrentRow.Parse();
						this.Caret.MoveDown(false);
						this.Caret.CurrentRow.Parse(true);

						this.Caret.Position.X = 0;
						this.Caret.SetPosition(this.Caret.Position);

						break;
					case IndentStyle.LastRow:
						indentationLevel = currentRow.GetLeadingWhitespace();

						int maximumLength = Math.Min(indentationLevel.Length, this.Caret.Position.X);
						string split = "\n" + indentationLevel.Substring(0, maximumLength);

						this.Document.InsertText(split, this.Caret.Position.X, this.Caret.Position.Y);
						this.Document.ResetVisibleRows();

						this.Caret.CurrentRow.Parse(true);
						this.Caret.MoveDown(false);
						this.Caret.CurrentRow.Parse(true);

						this.Caret.Position.X = indentationLevel.Length;
						this.Caret.SetPosition(this.Caret.Position);

						currentRow.Parse(true);
						currentRow.NextRow.Parse(true);

						break;
					case IndentStyle.Scope:
						currentRow.Parse(true);
						if (currentRow.ShouldOutdent == true)
							this.OutdentEndRow();

						this.Document.InsertText("\n", this.Caret.Position.X, this.Caret.Position.Y);
						this.Caret.CurrentRow.Parse();
						this.Caret.MoveDown(false);
						this.Caret.CurrentRow.Parse(false);

						if (keepTabs == true)
							indentationLevel = new string('\t', this.Caret.CurrentRow.Depth);
						else
							indentationLevel = new string(' ', tabSpaces * this.Caret.CurrentRow.Depth);

						this.Document.InsertText(indentationLevel, 0, this.Caret.Position.Y);
						this.Caret.CurrentRow.Parse(true);

						this.Caret.Position.X = indentationLevel.Length;
						this.Caret.SetPosition(this.Caret.Position);
						this.Caret.CropPosition();
						this.Selection.ClearSelection();

						currentRow.Parse(true);
						currentRow.NextRow.Parse(true);

						break;
				}

				this.ScrollIntoView();
			}
		}

		/// <summary>
		/// Outdents the end row.
		/// </summary>
		private void OutdentEndRow()
		{
			if (this.Indent == IndentStyle.Scope)
			{
				Row currentRow = this.Caret.CurrentRow;

				string indentation    = currentRow.Text.Substring(0, currentRow.GetLeadingWhitespace().Length);
				string newIndentation = "";

				if (keepTabs == true)
					newIndentation = new string('\t', this.Caret.CurrentRow.Depth);
				else
					newIndentation = new string(' ', tabSpaces * this.Caret.CurrentRow.Depth);

				TextRange textRange = new TextRange();
				textRange.FirstColumn = 0;
				textRange.LastColumn  = currentRow.GetLeadingWhitespace().Length;
				textRange.FirstRow    = currentRow.Index;
				textRange.LastRow     = currentRow.Index;

				this.Document.DeleteRange(textRange);
				this.Document.InsertText(newIndentation, 0, currentRow.Index, true);

				this.Caret.Position.X += newIndentation.Length;
				this.Caret.SetPosition(this.Caret.Position);
				this.Caret.CropPosition();

				this.Selection.ClearSelection();
				this.Caret.CurrentRow.Parse(true);
			}
		}

		/// <summary>
		/// Performs a delete action one step forward from the current caret position.
		/// </summary>
		private void DeleteForward()
		{
			this.Caret.CropPosition();
			if (this.Selection.IsValid == true)
				this.Selection.DeleteSelection();
			else
			{
				Row currentRow = this.Caret.CurrentRow;
				if (this.Caret.Position.X == currentRow.Text.Length)
				{
					if (this.Caret.Position.Y <= Document.Count - 2)
					{
						TextRange textRange   = new TextRange();
						textRange.FirstColumn = this.Caret.Position.X;
						textRange.FirstRow    = this.Caret.Position.Y;
						textRange.LastRow     = textRange.FirstRow + 1;
						textRange.LastColumn  = 0;

						this.Document.DeleteRange(textRange);
						this.Document.ResetVisibleRows();
					}
				}
				else
				{
					TextRange textRange   = new TextRange();
					textRange.FirstColumn = this.Caret.Position.X;
					textRange.FirstRow    = this.Caret.Position.Y;
					textRange.LastRow     = textRange.FirstRow;
					textRange.LastColumn  = textRange.FirstColumn + 1;

					this.Document.DeleteRange(textRange);
					this.Document.ResetVisibleRows();

					this.Caret.CurrentRow.Parse(true);
				}
			}
		}

		/// <summary>
		/// Performs a delete action one step backwards from the current caret position.
		/// </summary>
		private void DeleteBackwards()
		{
			this.Caret.CropPosition();
			if (this.Selection.IsValid == true)
				this.Selection.DeleteSelection();
			else
			{
				Row currentRow = this.Caret.CurrentRow;
				if (this.Caret.Position.X == 0)
				{
					if (this.Caret.Position.Y > 0)
					{
						this.Caret.Position.Y--;
						this.Caret.MoveEnd(false);

						this.DeleteForward();
						this.Document.ResetVisibleRows();
					}
				}
				else
				{
					if (this.Caret.Position.X >= currentRow.Text.Length)
					{
						TextRange textRange   = new TextRange();
						textRange.FirstColumn = this.Caret.Position.X - 1;
						textRange.FirstRow    = this.Caret.Position.Y;
						textRange.LastRow     = textRange.FirstRow;
						textRange.LastColumn  = textRange.FirstColumn + 1;

						if (this.Caret.Position.X - tabSpaces > -1)
						{
							if (this.Caret.CurrentRow.Text.Substring(this.Caret.Position.X - tabSpaces, tabSpaces) == new string(' ', tabSpaces))
							{
								textRange.FirstColumn = this.Caret.Position.X - tabSpaces;
								textRange.LastColumn  = this.Caret.Position.X;
							}
						}

						this.Document.DeleteRange(textRange);
						this.Document.ResetVisibleRows();

						this.Caret.MoveEnd(false);
						this.Caret.CurrentRow.Parse();
					}
					else
					{
						bool tabDeleted = false;

						TextRange textRange   = new TextRange();
						textRange.FirstColumn = this.Caret.Position.X - 1;
						textRange.FirstRow    = this.Caret.Position.Y;
						textRange.LastRow     = textRange.FirstRow;
						textRange.LastColumn  = textRange.FirstColumn + 1;

						if (this.Caret.Position.X - tabSpaces > -1)
						{
							if (this.Caret.CurrentRow.Text.Substring(this.Caret.Position.X - tabSpaces, tabSpaces) == new string(' ', tabSpaces))
							{
								textRange.FirstColumn = this.Caret.Position.X - tabSpaces;
								textRange.LastColumn  = this.Caret.Position.X;

								tabDeleted = true;
							}
						}

						this.Document.DeleteRange(textRange);
						this.Document.ResetVisibleRows();

						if (tabDeleted == false)
							this.Caret.MoveLeft(false);
						else
						{
							this.Caret.MoveEnd(false);
							this.Caret.MoveLeft(false);
						}

						this.Caret.CurrentRow.Parse();
					}
				}
			}
		}

		/// <summary>
		/// Scrolls the screen.
		/// </summary>
		/// <param name="scrollAmount">The scroll amount.</param>
		private void ScrollScreen(int scrollAmount)
		{
			this.ScrollScreen(scrollAmount, 2);
		}

		/// <summary>
		/// Scrolls the screen.
		/// </summary>
		/// <param name="scrollAmount">The scroll amount.</param>
		/// <param name="speed">The speed.</param>
		private void ScrollScreen(int scrollAmount, int speed)
		{
			tooltip.RemoveAll();
			int newScrollValue = vScroll.Value + scrollAmount;

			newScrollValue = Math.Max(newScrollValue, vScroll.Minimum);
			newScrollValue = Math.Min(newScrollValue, vScroll.Maximum);

			if (newScrollValue >= vScroll.Maximum - 2)
				newScrollValue = vScroll.Maximum - 2;

			vScroll.Value = newScrollValue;
			this.Redraw();
		}

		/// <summary>
		/// Pastes the text on the user's clipboard.
		/// </summary>
		private void PasteText()
		{
			IDataObject dataObject    = Clipboard.GetDataObject();
			string      clipboardText = "";

			if (dataObject.GetDataPresent(DataFormats.UnicodeText) == true)
				clipboardText = dataObject.GetData(DataFormats.UnicodeText) as string;
			else if (dataObject.GetDataPresent(DataFormats.Text) == true)
				clipboardText = dataObject.GetData(DataFormats.Text) as string;

			this.InsertText(clipboardText);
			if (this.ParseOnPaste == true)
				this.Document.ParseAll(true);
		}

		/// <summary>
		/// Begins a drag drop operation.
		/// </summary>
		private void BeginDragDrop()
		{
			this.DoDragDrop(this.Selection.Text, DragDropEffects.All);
		}

		/// <summary>
		/// Redraws this instance.
		/// </summary>
		private void Redraw()
		{
			this.Invalidate();
		}

		/// <summary>
		/// Redraws the caret.
		/// </summary>
		private void RedrawCaret()
		{
			using (Graphics gfx = this.CreateGraphics())
				this.Painter.RenderCaret(gfx);
		}

		/// <summary>
		/// Sets the mouse cursor.
		/// </summary>
		/// <param name="x">The x coordinate.</param>
		/// <param name="y">The y coordinate.</param>
		private void SetMouseCursor(int x, int y)
		{
			if (this.TextEditor.LockCursorUpdate == true)
			{
				this.Cursor = this.TextEditor.Cursor;
				return;
			}

			if (this.ViewPoint.Action == TextAction.DragText)
				this.Cursor = Cursors.Hand;
			else
			{
				if (x < this.ViewPoint.TotalMarginWidth)
				{
					if (x < this.ViewPoint.GutterMarginWidth)
						this.Cursor = Cursors.Arrow;
					else
					{
						Assembly assembly = this.GetType().Assembly;

						Stream cursorStream = assembly.GetManifestResourceStream("Storm.TextEditor.Resources.FlippedCursor.cur");
						this.Cursor = new Cursor(cursorStream);
					}
				}
				else
				{
					if (x > this.ViewPoint.TextMargin - 8)
					{
						if (this.IsOverSelection(x, y) == true && this.ViewPoint.Action != TextAction.Select)
							this.Cursor = Cursors.Arrow;
						else
						{
							TextPoint textPoint = this.Painter.GetTextPointAtPixel(x, y);
							Word textPointWord = this.Document.GetWordFromPos(textPoint);
							if (textPointWord != null)
							{
								WordMouseEventArgs wordMouseEventArgs = new WordMouseEventArgs();
								wordMouseEventArgs.Pattern = textPointWord.Pattern;
								wordMouseEventArgs.Button  = MouseButtons.None;
								wordMouseEventArgs.Cursor  = Cursors.IBeam;
								wordMouseEventArgs.Word    = textPointWord;

								this.TextEditor.OnWordMouseHover(ref wordMouseEventArgs);
								this.Cursor = wordMouseEventArgs.Cursor;
							}
							else
								this.Cursor = Cursors.IBeam;
						}
					}
					else
						this.Cursor = Cursors.Arrow;
				}
			}
		}

		/// <summary>
		/// Copies the selected text to the clipboard.
		/// </summary>
		private void CopyText()
		{
			if (this.Selection.IsValid == false)
				return;

			if (this.TextEditor.CopyAsRTF == true)
				this.CopyAsRtf();
			else
			{
				try
				{
					if (this.Selection != null)
					{
						Clipboard.SetDataObject(this.Selection.Text, true);
						CopyEventArgs copyEventArgs = new CopyEventArgs();

						copyEventArgs.Text = this.Selection.Text;
						this.OnClipboardUpdated(copyEventArgs);
					}
				}
				catch
				{
				}
			}
		}

		/// <summary>
		/// Gets the type of the char.
		/// </summary>
		/// <param name="characterString">The character string.</param>
		/// <returns>The type of the char.</returns>
		private int GetCharType(string characterString)
		{
			string firstType  = " \t";
			string secondType = ".,-+'?´=)(/&%¤#!\"\\<>[]$£@*:;{}";

			if (firstType.IndexOf(characterString) >= 0)
				return 1;

			if (secondType.IndexOf(characterString) >= 0)
				return 2;

			return 3;
		}

		/// <summary>
		/// Selects the pattern.
		/// </summary>
		/// <param name="rowIndex">Index of the row.</param>
		/// <param name="column">The column.</param>
		/// <param name="length">The length.</param>
		private void SelectPattern(int rowIndex, int column, int length)
		{
			this.Selection.Bounds.FirstColumn = column;
			this.Selection.Bounds.FirstRow    = rowIndex;
			this.Selection.Bounds.LastColumn  = column + length;
			this.Selection.Bounds.LastRow     = rowIndex;

			this.Caret.Position.X = column + length;
			this.Caret.Position.Y = rowIndex;
			this.Caret.CurrentRow.EnsureVisible();

			this.ScrollIntoView();
			this.Redraw();
		}

		/// <summary>
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new Container();
			ResourceManager resources = new ResourceManager(typeof(TextEditorBase));
			filler = new PictureBox();
			caretTimer = new WeakTimer(components);
			tooltip = new ToolTip(components);

			this.SuspendLayout();
			scrollHandler = new ScrollHandler();
			scrollHandler.Image = ((Bitmap)(resources.GetObject("ScrollHandler.Image")));
			scrollHandler.Location = new Point(197, 157);
			scrollHandler.Name = "scrollHandler";
			scrollHandler.Size = new Size(28, 28);
			scrollHandler.TabIndex = 4;
			scrollHandler.TransparencyKey = Color.FromArgb(255, 0, 255);
			scrollHandler.Visible = false;
			scrollHandler.EndScroll += this.OnScrollHandlerEndScroll;
			scrollHandler.BeginScroll += this.OnScrollHandlerBeginScroll;
			scrollHandler.Scroll += this.OnScrollHandlerScroll;

			hScroll.Cursor = Cursors.Default;
			hScroll.Scroll += this.OnHoritonzalScrollBarScroll;
			vScroll.Visible = false;

			vScroll.Cursor = Cursors.Default;
			vScroll.Scroll += this.OnVerticalScrollBarScroll;
			vScroll.Visible = false;

			caretTimer.Enabled = true;
			caretTimer.Interval = 500;
			caretTimer.Tick += this.OnCaretTimerTick;

			tooltip.AutoPopDelay = 50000;
			tooltip.InitialDelay = 0;
			tooltip.ReshowDelay = 1000;
			tooltip.ShowAlways = true;

			codeCompletionWindow = new CodeCompletionWindow(this.TextEditor);
			codeCompletionWindow.Visible = false;
			this.Controls.Add(codeCompletionWindow);

			this.TopThumb.BackColor = SystemColors.Control;
			this.TopThumb.Cursor = Cursors.HSplit;
			this.TopThumb.Location = new Point(101, 17);
			this.TopThumb.Name = "TopThumb";
			this.TopThumb.Size = new Size(16, 8);
			this.TopThumb.TabIndex = 3;
			this.TopThumb.Visible = false;

			this.LeftThumb.BackColor = SystemColors.Control;
			this.LeftThumb.Cursor = System.Windows.Forms.Cursors.VSplit;
			this.LeftThumb.Location = new Point(423, 17);
			this.LeftThumb.Name = "LeftThumb";
			this.LeftThumb.Size = new Size(8, 16);
			this.LeftThumb.TabIndex = 3;
			this.LeftThumb.Visible = false;

			this.AllowDrop = true;
			this.Controls.Add(scrollHandler);

			this.Size = new Size(240, 216);
			this.LostFocus += this.OnLeave;
			this.GotFocus += this.OnEnter;
			this.ResumeLayout(false);
		}

		/// <summary>
		/// Moves the caret to the next word.
		/// </summary>
		/// <param name="select">if set to <c>true</c> [select].</param>
		private void MoveCaretToNextWord(bool select)
		{
			int x = this.Caret.Position.X;
			int y = this.Caret.Position.Y;

			string currentCharacter     = "";
			int    currentCharacterType = 0;

			bool found = false;

			if (x == Caret.CurrentRow.Text.Length)
				currentCharacterType = 1;
			else
			{
				currentCharacter     = this.Document[y].Text.Substring(this.Caret.Position.X, 1);
				currentCharacterType = this.GetCharType(currentCharacter);
			}

			while (y < this.Document.Count)
			{
				while (x < this.Document[y].Text.Length)
				{
					string currentLocalCharacter     = this.Document[y].Text.Substring(x, 1);
					int    currentLocalCharacterType = this.GetCharType(currentLocalCharacter);

					if (currentLocalCharacterType != currentCharacterType)
					{
						if (currentLocalCharacterType == 1)
							currentCharacterType = 1;
						else
						{
							found = true;
							break;
						}
					}

					x++;
				}

				if (found == true)
					break;

				x = 0;
				y++;
			}

			if (y >= this.Document.Count - 1)
			{
				y = this.Document.Count - 1;

				if (x >= this.Document[y].Text.Length)
					x = this.Document[y].Text.Length - 1;

				if (x == -1)
					x = 0;
			}

			this.Caret.SetPosition(new TextPoint(x, y));
			if (select == false)
				this.Selection.ClearSelection();

			if (select == true)
				this.Selection.MakeSelection();

			this.ScrollIntoView();
		}

		/// <summary>
		/// Moves the caret to the previous word.
		/// </summary>
		/// <param name="select">if set to <c>true</c> [select].</param>
		private void MoveCaretToPreviousWord(bool select)
		{
			int x = this.Caret.Position.X;
			int y = this.Caret.Position.Y;

			string currentCharacter     = "";
			int    currentCharacterType = 0;

			bool found = false;

			if (x == this.Caret.CurrentRow.Text.Length)
			{
				currentCharacterType = 1;
				x = this.Caret.CurrentRow.Text.Length - 1;
			}
			else
			{
				currentCharacter     = this.Document[y].Text.Substring(this.Caret.Position.X, 1);
				currentCharacterType = this.GetCharType(currentCharacter);
			}

			while (y >= 0)
			{
				while (x >= 0 && x < this.Document[y].Text.Length)
				{
					string currentLocalCharacter     = this.Document[y].Text.Substring(x, 1);
					int    currentLocalCharacterType = this.GetCharType(currentLocalCharacter);

					if (currentLocalCharacterType != currentCharacterType)
					{
						found = true;

						string currentLoopCharacter     = this.Document[y].Text.Substring(x, 1);
						int    currentLoopCharacterType = currentLocalCharacterType;

						while (x > 0)
						{
							currentLoopCharacter     = this.Document[y].Text.Substring(x, 1);
							currentLoopCharacterType = this.GetCharType(currentLoopCharacter);
							if (currentLoopCharacterType != currentLocalCharacterType)
							{
								x++;
								break;
							}

							x--;
						}

						break;
					}

					x--;
				}

				if (found == true)
					break;

				if (y == 0)
				{
					x = 0;
					break;
				}

				y--;
				x = this.Document[y].Text.Length - 1;
			}

			this.Caret.SetPosition(new TextPoint(x, y));
			if (select == false)
				this.Selection.ClearSelection();

			if (select == true)
				this.Selection.MakeSelection();

			this.ScrollIntoView();
		}

		/// <summary>
		/// Copies the current selection as Rtf format.
		/// </summary>
		private void CopyAsRtf()
		{
			TextStyle[] textStyles = this.Document.Parser.Language.Styles;
			this.Document.ParseAll(true);

			int firstRowIndex = Selection.LogicalBounds.FirstRow;
			int lastRowIndex  = Selection.LogicalBounds.LastRow;

			int firstColumnIndex = Selection.LogicalBounds.FirstColumn;
			int lastColumnIndex  = Selection.LogicalBounds.LastColumn;

			StringBuilder rtfBuilder = new StringBuilder();
			rtfBuilder.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1053{\fonttbl{\f0\fmodern\fprq1\fcharset0 " + this.FontName + @";}}");
			rtfBuilder.Append(@"{\colortbl ;");

			foreach (TextStyle textStyle in textStyles)
			{
				rtfBuilder.AppendFormat("\\red{0}\\green{1}\\blue{2};", textStyle.ForeColor.R, textStyle.ForeColor.G, textStyle.ForeColor.B);
				rtfBuilder.AppendFormat("\\red{0}\\green{1}\\blue{2};", textStyle.BackColor.R, textStyle.BackColor.G, textStyle.BackColor.B);
			}

			rtfBuilder.Append(@";}");
			rtfBuilder.Append(@"\viewkind4\uc1\pard\f0\fs20");

			bool doneLooping = false;
			for (int rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++)
			{
				Row currentRow = this.Document[rowIndex];

				foreach (Word currentWord in currentRow)
				{
					if (rowIndex == firstRowIndex && currentWord.Column + currentWord.Text.Length < firstColumnIndex)
						continue;

					bool rowIsFirst = (rowIndex == firstRowIndex && currentWord.Column <= firstColumnIndex && currentWord.Column + currentWord.Text.Length > firstColumnIndex);
					bool rowIsLast  = (rowIndex == lastRowIndex  && currentWord.Column < lastColumnIndex   && currentWord.Column + currentWord.Text.Length > lastColumnIndex);

					if (currentWord.Type == WordType.Word && currentWord.Style != null)
					{
						int currentTextStyleIndex = Array.IndexOf(textStyles, currentWord.Style);
						currentTextStyleIndex *= 2;
						currentTextStyleIndex++;

						rtfBuilder.Append("{\\cf" + currentTextStyleIndex.ToString());
						if (currentWord.Style.Transparent == false)
							rtfBuilder.Append("\\highlight" + (currentTextStyleIndex + 1).ToString());

						rtfBuilder.Append(" ");
					}

					if (currentWord.Style != null)
					{
						if (currentWord.Style.Bold == true)
							rtfBuilder.Append(@"\b ");

						if (currentWord.Style.Underline == true)
							rtfBuilder.Append(@"\ul ");

						if (currentWord.Style.Italic == true)
							rtfBuilder.Append(@"\i ");
					}

					string currentWordText = currentWord.Text;

					if (rowIsFirst == true)
						currentWordText = currentWordText.Substring(firstColumnIndex - currentWord.Column);

					if (rowIsLast == true)
						currentWordText = currentWordText.Substring(0, lastColumnIndex - currentWord.Column);

					currentWordText = currentWordText.Replace(@"\", @"\\").Replace(@"}", @"\}").Replace(@"{", @"\{");

					rtfBuilder.Append(currentWordText);
					if (currentWord.Style != null)
					{
						if (currentWord.Style.Bold == true)
							rtfBuilder.Append(@"\b0 ");

						if (currentWord.Style.Underline == true)
							rtfBuilder.Append(@"\ul0 ");

						if (currentWord.Style.Italic == true)
							rtfBuilder.Append(@"\i0 ");
					}

					if (currentWord.Type == WordType.Word && currentWord.Style != null)
						rtfBuilder.Append("}");

					if (rowIsLast == true)
					{
						doneLooping = true;
						break;
					}
				}

				if (doneLooping == true)
					break;

				rtfBuilder.Append(@"\par");
			}

			DataObject dataObject = new DataObject();
			dataObject.SetData(DataFormats.Rtf, rtfBuilder.ToString());

			string selectionText = this.Selection.Text;
			dataObject.SetData(DataFormats.Text, selectionText);
			Clipboard.SetDataObject(dataObject);

			CopyEventArgs copyEventArgs = new CopyEventArgs();
			copyEventArgs.Text = selectionText;
			this.OnClipboardUpdated(copyEventArgs);
		}

		/// <summary>
		/// Selects the current word.
		/// </summary>
		private void SelectCurrentWord()
		{
			Row currentRow = this.Caret.CurrentRow;
			if (currentRow.Text == "")
				return;

			if (this.Caret.Position.X >= currentRow.Text.Length)
				return;

			string currentCaretCharacter = currentRow.Text.Substring(this.Caret.Position.X, 1);
			int characterType = this.GetCharType(currentCaretCharacter);

			int leftPosition = this.Caret.Position.X;
			int rightPosition = this.Caret.Position.X;

			while (leftPosition >= 0 && this.GetCharType(currentRow.Text.Substring(leftPosition, 1)) == characterType)
				leftPosition--;

			while (rightPosition <= currentRow.Text.Length - 1 && this.GetCharType(currentRow.Text.Substring(rightPosition, 1)) == characterType)
				rightPosition++;

			this.Selection.Bounds.FirstRow = this.Selection.Bounds.LastRow = currentRow.Index;
			this.Selection.Bounds.FirstColumn = leftPosition + 1;
			this.Selection.Bounds.LastColumn = rightPosition;

			this.Caret.Position.X = rightPosition;
			this.Caret.SetPosition(Caret.Position);

			this.Redraw();
		}

		#endregion

		#region Public

		/// <summary>
		/// Removes the focus.
		/// </summary>
		public void RemoveFocus()
		{
			if (this.ContainsFocus == false)
			{
				caretTimer.Enabled = false;
				this.Caret.Blink = false;
			}

			this.Redraw();
		}

		/// <summary>
		/// Setups the view point members of the TextEditorBase.
		/// </summary>
		public void SetupViewPoint()
		{
			if (this.ViewPoint.RowHeight == 0)
				this.ViewPoint.RowHeight = 48;

			if (this.ViewPoint.CharWidth == 0)
				this.ViewPoint.CharWidth = 16;

			this.ViewPoint.FirstVisibleColumn = hScroll.Value;
			this.ViewPoint.FirstVisibleRow = vScroll.Value;

			this.ViewPoint.VisibleRowCount = 0;
			if (hScroll.Visible == true)
				this.ViewPoint.VisibleRowCount = (this.Height - hScroll.Height) / this.ViewPoint.RowHeight + 1;
			else
				this.ViewPoint.VisibleRowCount = (this.Height - hScroll.Height) / this.ViewPoint.RowHeight + 2;

			if (this.ShowGutterMargin == true)
				this.ViewPoint.GutterMarginWidth = this.GutterMarginWidth;
			else
				this.ViewPoint.GutterMarginWidth = 0;

			if (this.ShowLineNumbers == true)
			{
				int    rowCount         = (this.Document.Count).ToString().Length;
				string lineNumberString = new string('9', rowCount);

				this.ViewPoint.LineNumberMarginWidth = 25 + this.Painter.MeasureString(lineNumberString).Width;
			}
			else
				this.ViewPoint.LineNumberMarginWidth = 0;

			this.ViewPoint.TotalMarginWidth = this.ViewPoint.GutterMarginWidth + this.ViewPoint.LineNumberMarginWidth;
			if (this.Document.Folding == true)
				this.ViewPoint.TextMargin = this.ViewPoint.TotalMarginWidth + 20;
			else
				this.ViewPoint.TextMargin = this.ViewPoint.TotalMarginWidth + 7;

			this.ViewPoint.ClientAreaWidth = this.Width - vScroll.Width - this.ViewPoint.TextMargin;
			this.ViewPoint.ClientAreaStart = this.ViewPoint.FirstVisibleColumn * this.ViewPoint.CharWidth;
		}

		/// <summary>
		/// Gets the pixel at the text point.
		/// </summary>
		/// <param name="textPoint">The text point.</param>
		/// <returns>The found point.</returns>
		public Point GetPixelAtTextPoint(TextPoint textPoint)
		{
			return this.Painter.GetPixelAtTextPoint(textPoint);
		}

		/// <summary>
		/// Calculates the maximum character width.
		/// </summary>
		public void CalculateMaxCharWidth()
		{
			maxCharWidth = this.Painter.GetMaxCharWidth();
		}

		/// <summary>
		/// Sets the maximum amount of "scrolling space" available to the horizontal ScrollBar.
		/// </summary>
		public void SetMaxHorizontalScroll()
		{
			this.CalculateMaxCharWidth();

			int charWidth = this.ViewPoint.CharWidth;
			if (charWidth == 0)
				charWidth = 1;

			if (this.ViewPoint.ClientAreaWidth / charWidth < 0)
			{
				hScroll.Maximum = 1000;
				return;
			}

			hScroll.LargeChange = this.ViewPoint.ClientAreaWidth / charWidth;

			try
			{
				int maximumLength = 0;
				for (int rowCount = this.ViewPoint.FirstVisibleRow; rowCount < this.Document.VisibleRows.Count; rowCount++)
				{
					if (rowCount >= this.ViewPoint.VisibleRowCount + this.ViewPoint.FirstVisibleRow)
						break;

					string rowText = "";
					if (this.Document.VisibleRows[rowCount].IsCollapsed == true)
						rowText = this.Document.VisibleRows[rowCount].VirtualCollapsedRow.Text;
					else
						rowText = this.Document.VisibleRows[rowCount].Text;

					rowText = rowText.Replace("\t", new string(' ', this.TabSpaces));
					if (rowText.Length > maximumLength)
						maximumLength = rowText.Length;
				}

				int pixels = maximumLength * maxCharWidth;
				int chars = pixels / charWidth;

				if (hScroll.Value <= chars)
					hScroll.Maximum = chars;
			}
			catch
			{
				hScroll.Maximum = 1000;
			}

			if (hScroll.Maximum > hScroll.LargeChange)
				hScroll.Show();
			else
				hScroll.Hide();
		}

		/// <summary>
		/// Sets up the ScrollBars.
		/// </summary>
		public void InitScrollbars()
		{
			if (this.Document.VisibleRows.Count > 0)
			{
				vScroll.Maximum = Document.VisibleRows.Count + 1;
				vScroll.LargeChange = ViewPoint.VisibleRowCount;
				this.SetMaxHorizontalScroll();
			}
			else
				vScroll.Maximum = 1;

			if (vScroll.Maximum > vScroll.LargeChange)
				vScroll.Show();
			else
				vScroll.Hide();
		}

		/// <summary>
		/// Initializes the TextEditorBase's Painter's graphics.
		/// </summary>
		public void InitGraphics()
		{
			this.Painter.InitGraphics();
		}

		/// <summary>
		/// Inserts the specified text at the caret's current position.
		/// </summary>
		/// <param name="text">Text to insert.</param>
		public void InsertText(string text)
		{
			this.Caret.CropPosition();
			if (this.Selection.IsValid == true)
			{
				this.Selection.DeleteSelection();
				this.InsertText(text);
			}
			else
			{
				if (overWrite == false || text.Length > 1)
				{
					TextPoint textPoint = this.Document.InsertText(text, this.Caret.Position.X, this.Caret.Position.Y);
					this.Caret.CurrentRow.Parse(true);

					if (text.Length == 1)
					{
						this.Caret.SetPosition(textPoint);
						this.Caret.CaretMoved(false);
					}
					else
					{
						this.Document.ResetVisibleRows();
						this.Caret.SetPosition(textPoint);
						this.Caret.CaretMoved(false);
					}
				}
				else
				{
					TextRange textRange   = new TextRange();
					textRange.FirstColumn = this.Caret.Position.X;
					textRange.FirstRow    = this.Caret.Position.Y;
					textRange.LastColumn  = this.Caret.Position.X + 1;
					textRange.LastRow     = this.Caret.Position.Y;

					UndoBlockCollection undoBlockCollection = new UndoBlockCollection();
					UndoBlock           undoBlock           = new UndoBlock();

					undoBlock.Action   = UndoAction.DeleteRange;
					undoBlock.Text     = this.Document.GetRange(textRange);
					undoBlock.Position = this.Caret.Position;

					undoBlockCollection.Add(undoBlock);
					this.Document.DeleteRange(textRange, false);

					undoBlock        = new UndoBlock();
					undoBlock.Action = UndoAction.InsertRange;

					undoBlock.Text     = text;
					undoBlock.Position = this.Caret.Position;
					undoBlockCollection.Add(undoBlock);

					this.Document.AddToUndoList(undoBlockCollection);
					this.Document.InsertText(text, this.Caret.Position.X, this.Caret.Position.Y, false);

					this.Caret.CurrentRow.Parse(true);
					this.Caret.MoveRight(false);
				}
			}
		}

		/// <summary>
		/// Gets all keywords of the current language and returns them in a list.
		/// </summary>
		/// <returns>List containing all found keywords.</returns>
		public ArrayList ExtractKeywords()
		{
			ArrayList keywordList = new ArrayList();
			Language  language    = this.Document.Parser.Language;

			for (int i = 0; i <= language.Blocks.Length - 1; i++)
			{
				Block block = language.Blocks[i];
				if (block.KeywordsList.Count > 0)
				{
					for (int keywordIndex = 0; keywordIndex <= block.KeywordsList.Count - 1; keywordIndex++)
					{
						PatternList patternList = block.KeywordsList[keywordIndex];
						foreach (string key in patternList.SimplePatterns.Keys)
						{
							if (keywordList.Contains(key) == false)
								keywordList.Add(key);
						}

					}
				}
			}

			return keywordList;
		}

		/// <summary>
		/// Shows the Goto Line dialog.
		/// </summary>
		public void ShowGotoLine()
		{
			GotoLine gotoLineDialog = new GotoLine(this, this.Document.Count);
			gotoLineDialog.ShowDialog(this.TopLevelControl);
		}

		/// <summary>
		/// Selects the specified row and scrolls to it.
		/// </summary>
		/// <param name="rowIndex">Row to select and scroll to.</param>
		public void GotoLine(int rowIndex)
		{
			if (rowIndex >= this.Document.Count)
				rowIndex = this.Document.Count - 1;

			if (rowIndex < 0)
				rowIndex = 0;

			this.Caret.Position.Y = rowIndex;
			this.Caret.Position.X = 0;
			this.Caret.CurrentRow.EnsureVisible();

			this.ClearSelection();
			this.ScrollIntoView();
			this.Redraw();
		}

		/// <summary>
		/// Clears the selection.
		/// </summary>
		public void ClearSelection()
		{
			this.Selection.ClearSelection();
			this.Redraw();
		}

		/// <summary>
		/// Returns whether the X and Y coordinates is over the current selection.
		/// </summary>
		/// <param name="x">The X coordinate.</param>
		/// <param name="y">The Y coordinate.</param>
		/// <returns>Whether the X and Y coordinates is the over the current selection.</returns>
		public bool IsOverSelection(int x, int y)
		{
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(x, y);
			if (textPoint.Y >= this.Selection.LogicalBounds.FirstRow && textPoint.Y <= this.Selection.LogicalBounds.LastRow && this.Selection.IsValid == true)
			{
				if (textPoint.Y > this.Selection.LogicalBounds.FirstRow && textPoint.Y < this.Selection.LogicalBounds.LastRow && this.Selection.IsValid == true)
					return true;
				else
				{
					if (textPoint.Y == this.Selection.LogicalBounds.FirstRow && this.Selection.LogicalBounds.FirstRow == this.Selection.LogicalBounds.LastRow)
					{
						if (textPoint.X >= this.Selection.LogicalBounds.FirstColumn && textPoint.X <= this.Selection.LogicalBounds.LastColumn)
							return true;
						else
							return false;
					}
					else if (textPoint.X >= this.Selection.LogicalBounds.FirstColumn && textPoint.Y == this.Selection.LogicalBounds.FirstRow)
						return true;
					else if (textPoint.X <= this.Selection.LogicalBounds.LastColumn && textPoint.Y == this.Selection.LogicalBounds.LastRow)
						return true;
					else
						return false;
				}
			}
			else
				return false;
		}

		/// <summary>
		/// If the specified TextPoint isn't visible, scrolls to it.
		/// </summary>
		/// <param name="textPoint">TextPoint to scroll to.</param>
		public void ScrollIntoView(TextPoint textPoint)
		{
			TextPoint tempTextPoint = this.Caret.Position;
			this.Caret.Position = textPoint;

			if (this.Caret.CurrentRow != null)
				this.Caret.CurrentRow.EnsureVisible();

			this.ScrollIntoView();
			this.Caret.Position = tempTextPoint;
			this.Invalidate();
		}

		/// <summary>
		/// If the specified Row isn't visible, scrolls to it.
		/// </summary>
		/// <param name="rowIndex">Row to scroll to.</param>
		public void ScrollIntoView(int rowIndex)
		{
			Row row = this.Document[rowIndex];
			row.EnsureVisible();

			vScroll.Value = row.VisibleIndex;
			this.Invalidate();
		}

		/// <summary>
		/// If the current caret position is out of view, scrolls to it.
		/// </summary>
		public void ScrollIntoView()
		{
			this.InitScrollbars();
			this.Caret.CropPosition();
			try
			{
				Row currentRow = this.Caret.CurrentRow;
				if (currentRow.VisibleIndex >= this.ViewPoint.FirstVisibleRow + this.ViewPoint.VisibleRowCount - 2)
				{
					int difference = this.Caret.CurrentRow.VisibleIndex - (this.ViewPoint.FirstVisibleRow + this.ViewPoint.VisibleRowCount - 2) + 
						this.ViewPoint.FirstVisibleRow;

					if (difference > this.Document.VisibleRows.Count - 1)
						difference = this.Document.VisibleRows.Count - 1;

					Row visibleRow = this.Document.VisibleRows[difference];
					int index = visibleRow.VisibleIndex;
					if (index != -1)
						vScroll.Value = index;
				}
			}
			catch
			{
			}

			if (this.Caret.CurrentRow.VisibleIndex < this.ViewPoint.FirstVisibleRow)
			{
				Row currentRow = this.Caret.CurrentRow;
				int index = currentRow.VisibleIndex;
				if (index != -1)
					vScroll.Value = index;
			}

			int x = 0;
			Row row = this.Caret.CurrentRow;
			if (Caret.CurrentRow.IsCollapsedEndPart)
			{
				x  = this.Painter.MeasureRow(row, this.Caret.Position.X).Width + this.Caret.CurrentRow.ExpansionPixelStart;
				x -= this.Painter.MeasureRow(row, row.ExpansionStartChar).Width;

				if (x >= this.ViewPoint.ClientAreaWidth + this.ViewPoint.ClientAreaStart)
					hScroll.Value = Math.Min(hScroll.Maximum, ((x - this.ViewPoint.ClientAreaWidth) / this.ViewPoint.CharWidth) + 15);

				if (x < this.ViewPoint.ClientAreaStart + 10)
					hScroll.Value = Math.Max(hScroll.Minimum, ((x) / this.ViewPoint.CharWidth) - 15);
			}
			else
			{
				x = this.Painter.MeasureRow(row, this.Caret.Position.X).Width;

				if (x >= this.ViewPoint.ClientAreaWidth + this.ViewPoint.ClientAreaStart)
					hScroll.Value = Math.Min(hScroll.Maximum, ((x - this.ViewPoint.ClientAreaWidth) / this.ViewPoint.CharWidth) + 15);

				if (x < this.ViewPoint.ClientAreaStart)
					hScroll.Value = Math.Max(hScroll.Minimum, (x / this.ViewPoint.CharWidth) - 15);
			}
		}

		/// <summary>
		/// Scrolls to the next bookmark.
		/// </summary>
		public void GotoNextBookmark()
		{
			int index = 0;
			index = this.Document.GetNextBookmark(this.Caret.Position.Y);

			this.Caret.SetPosition(new TextPoint(0, index));
			this.ScrollIntoView();
			this.Redraw();
		}

		/// <summary>
		/// Scrolls to the previous bookmark.
		/// </summary>
		public void GotoPreviousBookmark()
		{
			int index = 0;
			index = this.Document.GetPreviousBookmark(this.Caret.Position.Y);

			this.Caret.SetPosition(new TextPoint(0, index));
			this.ScrollIntoView();
			this.Redraw();
		}

		/// <summary>
		/// Selects the next match depending on the specified conditions.
		/// </summary>
		/// <param name="Pattern">Pattern to search for.</param>
		/// <param name="matchCase">Whether the pattern should match case.</param>
		/// <param name="wholeWords">Whether the pattern should be a whole word.</param>
		/// <param name="useRegularExpressions">Whether to use regular expressions when searching.</param>
		/// <returns>Found match.</returns>
		public bool SelectNext(string pattern, bool matchCase, bool wholeWords, bool useRegularExpressions)
		{
			string newPattern = pattern;
			int startColumn = this.Caret.Position.X;
			int startRow    = this.Caret.Position.Y;

			for (int rowIndex = this.Caret.Position.Y; rowIndex < this.Document.Count; rowIndex++)
			{
				Row row = this.Document[rowIndex];
				if (useRegularExpressions == true)
				{
					Regex regex = new Regex(pattern);
					int matchColumn = startColumn;
					if (rowIndex == startRow)
					{
						if (startColumn < row.Text.Length)
							continue;
					}
					else
						matchColumn = 0;

					Match match = regex.Match(row.Text, matchColumn);
					if (match.Success == true)
					{
						this.SelectPattern(rowIndex, match.Index, match.Length);
						return true;
					}
				}
				else
				{
					int column = -1;
					string spacedText = "";
					string rowText = row.Text;
					if (wholeWords == true)
					{
						spacedText = " " + row.Text + " ";
						rowText = "";
						newPattern = " " + pattern + " ";
						foreach (char character in spacedText)
						{
							if (".,+-*^\\/()[]{}@:;'?£$#%& \t=<>".IndexOf(character) >= 0)
								rowText += " ";
							else
								rowText += character;
						}
					}

					if (matchCase == false)
					{
						rowText = rowText.ToLower();
						newPattern = newPattern.ToLower();
					}

					column = rowText.IndexOf(newPattern);
					if (column >= startColumn || (rowIndex > startRow && column >= 0))
					{
						this.SelectPattern(rowIndex, column, pattern.Length);
						return true;
					}
				}
			}

			return false;
		}

		/// <summary>
		/// Replaces the current selection with the specified text.
		/// </summary>
		/// <param name="text">Text to replace selection with.</param>
		/// <returns>Whether the replacing was successful.</returns>
		public bool ReplaceSelection(string text)
		{
			if (this.Selection.IsValid == false)
				return false;

			int x = this.Selection.LogicalBounds.FirstColumn;
			int y = this.Selection.LogicalBounds.FirstRow;

			TextRange textRange = new TextRange(this.Selection.Bounds.FirstColumn, this.Selection.Bounds.FirstRow,
				this.Selection.Bounds.LastColumn, this.Selection.Bounds.LastRow);

			TextRange newTextRange = this.Document.ReplaceRange(textRange, text, true);
			this.Selection.Bounds = newTextRange;
			this.Caret.Position.X = newTextRange.LastColumn;
			this.Caret.Position.Y = newTextRange.LastRow;

			return true;
		}

		/// <summary>
		/// Toggles a bookmark at the line the user is currently on.
		/// </summary>
		public void ToggleBookmark()
		{
			this.Document[this.Caret.Position.Y].Bookmarked = this.Document[this.Caret.Position.Y].Bookmarked == false;
			this.Redraw();
		}

		/// <summary>
		/// Deletes the letter after the user's current selection.
		/// </summary>
		public void Delete()
		{
			this.DeleteForward();
			this.Refresh();
		}

		/// <summary>
		/// Selects all the content of the TextEditorBase.
		/// </summary>
		public void SelectAll()
		{
			this.Selection.SelectAll();
			this.Redraw();
		}

		/// <summary>
		/// Pastes the user's clipboard to the user's current selection. (If the clipboard content is valid)
		/// </summary>
		public void Paste()
		{
			this.PasteText();
			this.Refresh();
		}

		/// <summary>
		/// Copies the user's current selection to his/her clipboard.
		/// </summary>
		public void Copy()
		{
			this.CopyText();
		}

		/// <summary>
		/// Copies the user's current selection to his/her clipboard and deletes the selection.
		/// </summary>
		public void Cut()
		{
			this.CopyText();
			this.Selection.DeleteSelection();
		}

		/// <summary>
		/// Removes the row the user is currently on.
		/// </summary>
		public void RemoveCurrentRow()
		{
			if (this.Caret.CurrentRow != null && this.Document.Count > 1)
			{
				this.Document.Remove(this.Caret.CurrentRow.Index, true);
				this.Document.ResetVisibleRows();

				this.Caret.CropPosition();
				this.Caret.CurrentRow.Text = Caret.CurrentRow.Text;
				this.Caret.CurrentRow.Parse(true);

				this.Document.ResetVisibleRows();
				this.ScrollIntoView();
			}
		}

		/// <summary>
		/// If the current selection is valid, does a Cut action, else, removes the current row.
		/// </summary>
		public void CutClear()
		{
			if (this.Selection.IsValid == true)
				this.Cut();
			else
				this.RemoveCurrentRow();
		}

		/// <summary>
		/// Redoes the last undone action by the user.
		/// </summary>
		public void Redo()
		{
			TextPoint textPoint = this.Document.Redo();
			if (textPoint.X != -1 && textPoint.Y != -1)
			{
				this.Caret.Position = textPoint;
				this.Selection.ClearSelection();
				this.ScrollIntoView();
			}
		}

		/// <summary>
		/// Undoes the last action by the user.
		/// </summary>
		public void Undo()
		{
			TextPoint textPoint = this.Document.Undo();
			if (textPoint.X != -1 && textPoint.Y != -1)
			{
				this.Caret.Position = textPoint;
				this.Selection.ClearSelection();
				this.ScrollIntoView();
			}
		}

		/// <summary>
		/// Gets the text point at the given coordinates.
		/// </summary>
		/// <param name="x">The x coordinate.</param>
		/// <param name="y">The y coordinate.</param>
		/// <returns>The found text point.</returns>
		public TextPoint GetTextPointAtPixel(int x, int y)
		{
			return this.Painter.GetTextPointAtPixel(x, y);
		}

		/// <summary>
		/// Shows the Find form.
		/// </summary>
		public void ShowFind()
		{
			if (this.FindReplaceDialog != null)
			{
				this.FindReplaceDialog.TopLevel = true;
				if (this.TopLevelControl is Form)
					this.FindReplaceDialog.Owner = this.TopLevelControl as Form;

				this.FindReplaceDialog.ShowFind();
			}
		}

		/// <summary>
		/// Shows the Replace form.
		/// </summary>
		public void ShowReplace()
		{
			if (this.FindReplaceDialog != null)
			{
				this.FindReplaceDialog.TopLevel = true;
				if (this.TopLevelControl is Form)
					this.FindReplaceDialog.Owner = this.TopLevelControl as Form;

				this.FindReplaceDialog.ShowReplace();
			}
		}

		/// <summary>
		/// Finds the next match.
		/// </summary>
		public void FindNext()
		{
			this.FindReplaceDialog.FindNext();
		}

		/// <summary>
		/// Scrolls to the specified row and centers it in the TextEditorBase.
		/// </summary>
		/// <param name="rowIndex">Row to scroll to and center.</param>
		public void CenterInView(int rowIndex)
		{
			if (this.Document == null && rowIndex > this.Document.Count && rowIndex < 0)
				return;

			Row row = this.Document[rowIndex];
			int topRow = 0;
			int visibleLines = 0;

			if (row != null)
			{
				this.InitScrollbars();
				row.EnsureVisible();

				visibleLines = this.ViewPoint.VisibleRowCount / 2;
				if (row.VisibleIndex < visibleLines)
					topRow = 0;
				else
					topRow = row.VisibleIndex - visibleLines;

				vScroll.Value = topRow;
				this.Invalidate();
			}
		}

		#endregion

		#region Protected

		/// <summary>
		/// Processes Windows messages from the OS.
		/// </summary>
		/// <param name="m">Windows message to process.</param>
		protected override void WndProc(ref Message m)
		{
			if (m.Msg == (int)WindowMessage.WM_DESTROY)
			{
				try
				{
					if (this.FindReplaceDialog != null)
						this.FindReplaceDialog.Close();
				}
				catch
				{
				}
			}

			base.WndProc(ref m);
		}

		/// <summary>
		/// Releases unmanaged and - optionally - managed resources
		/// </summary>
		/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
		protected override void Dispose(bool disposing)
		{
			this.RemoveFocus();
			if (disposing == true)
			{
				if (components != null)
					components.Dispose();

				if (this.Painter != null)
					this.Painter.Dispose();
			}

			base.Dispose(disposing);
		}

		#endregion

		#region EventHandlers

		/// <summary>
		/// Called when [scroll handler begin scroll].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnScrollHandlerBeginScroll(object sender, EventArgs e) {
			scrollHandlerCurrentPosition = 0;
			this.ViewPoint.YOffset = 0;
		}

		/// <summary>
		/// Called when [scroll handler end scroll].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnScrollHandlerEndScroll(object sender, EventArgs e) {
			this.ViewPoint.YOffset = 0;
			this.Redraw();
		}

		/// <summary>
		/// Called when [scroll handler scroll].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="Storm.TextEditor.Interacting.Scrolling.ScrollEventArgs"/> instance containing the event data.</param>
		private void OnScrollHandlerScroll(object sender, ScrollEventArgs e) {
			if (e.DeltaY < 0 && vScroll.Value == 0) {
				this.ViewPoint.YOffset = 0;
				this.Redraw();

				return;
			}

			if (e.DeltaY > 0 && vScroll.Value >= vScroll.Maximum - this.ViewPoint.VisibleRowCount + 1) {
				this.ViewPoint.YOffset = 0;
				this.Redraw();

				return;
			}

			scrollHandlerCurrentPosition += (double)e.DeltaY / (double)8;

			int rowScrollCount = (int)scrollHandlerCurrentPosition / this.ViewPoint.RowHeight;
			if (rowScrollCount != 0)
				scrollHandlerCurrentPosition -= rowScrollCount * this.ViewPoint.RowHeight;

			this.ViewPoint.YOffset = -(int)scrollHandlerCurrentPosition;
			this.ScrollScreen(rowScrollCount);
		}

		/// <summary>
		/// Raises the <see cref="E:ClipboardUpdated"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.CopyEventArgs"/> instance containing the event data.</param>
		private void OnClipboardUpdated(CopyEventArgs e)
		{
			if (this.ClipboardUpdated != null)
				this.ClipboardUpdated(this, e);
		}

		/// <summary>
		/// Raises the <see cref="E:RowMouseDown"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.RowMouseEventArgs"/> instance containing the event data.</param>
		private void OnRowMouseDown(RowMouseEventArgs e)
		{
			if (this.RowMouseDown != null)
				this.RowMouseDown(this, e);
		}

		/// <summary>
		/// Raises the <see cref="E:RowMouseMove"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.RowMouseEventArgs"/> instance containing the event data.</param>
		private void OnRowMouseMove(RowMouseEventArgs e)
		{
			if (this.RowMouseMove != null)
				this.RowMouseMove(this, e);
		}

		/// <summary>
		/// Raises the <see cref="E:RowMouseUp"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.RowMouseEventArgs"/> instance containing the event data.</param>
		private void OnRowMouseUp(RowMouseEventArgs e)
		{
			if (this.RowMouseUp != null)
				this.RowMouseUp(this, e);
		}

		/// <summary>
		/// Raises the <see cref="E:RowClick"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.RowMouseEventArgs"/> instance containing the event data.</param>
		private void OnRowClick(RowMouseEventArgs e)
		{
			if (this.RowClick != null)
				this.RowClick(this, e);
		}

		/// <summary>
		/// Raises the <see cref="E:RowDoubleClick"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.RowMouseEventArgs"/> instance containing the event data.</param>
		private void OnRowDoubleClick(RowMouseEventArgs e)
		{
			if (this.RowDoubleClick != null)
				this.RowDoubleClick(this, e);
		}

		/// <summary>
		/// Raises the Load event.
		/// </summary>
		/// <param name="e">EventArgs.</param>
		protected override void OnLoad(EventArgs e)
		{
			this.UpdateContent();
			this.Refresh();
		}

		/// <summary>
		/// Called when [parse].
		/// </summary>
		internal void OnParse()
		{
			this.Redraw();
		}

		/// <summary>
		/// Called when [change].
		/// </summary>
		internal void OnChange()
		{
			if (this.Caret.Position.Y > this.Document.Count - 1)
			{
				this.Caret.Position.Y = this.Document.Count - 1;
				this.ScrollIntoView();
			}

			if (this.VirtualWhitespace == false && this.Caret.CurrentRow != null && this.Caret.Position.X > this.Caret.CurrentRow.Text.Length)
			{
				this.Caret.Position.X = this.Caret.CurrentRow.Text.Length;
				this.Redraw();
			}

			if (this.ContainsFocus == false)
				this.Selection.ClearSelection();

			if (this.Selection.LogicalBounds.FirstRow > this.Document.Count)
			{
				this.Selection.Bounds.FirstColumn = Caret.Position.X;
				this.Selection.Bounds.LastColumn  = Caret.Position.X;
				this.Selection.Bounds.FirstRow    = Caret.Position.Y;
				this.Selection.Bounds.LastRow     = Caret.Position.Y;
			}

			if (this.Selection.LogicalBounds.LastRow > this.Document.Count)
			{
				this.Selection.Bounds.FirstColumn = this.Caret.Position.X;
				this.Selection.Bounds.LastColumn  = this.Caret.Position.X;
				this.Selection.Bounds.FirstRow    = this.Caret.Position.Y;
				this.Selection.Bounds.LastRow     = this.Caret.Position.Y;
			}

			this.Redraw();
		}

		/// <summary>
		/// Determines whether the specified key is a regular input key or a special key that requires preprocessing.
		/// </summary>
		/// <param name="keyData">One of the <see cref="T:System.Windows.Forms.Keys"/> values.</param>
		/// <returns>
		/// true if the specified key is a regular input key; otherwise, false.
		/// </returns>
		protected override bool IsInputKey(Keys keyData)
		{
			switch (keyData & Keys.KeyCode)
			{
				case Keys.Up:
					return true;
				case Keys.Down:
					return true;
				case Keys.Right:
					return true;
				case Keys.Left:
					return true;
				case Keys.Tab:
					return true;
				default:
					return base.IsInputKey(keyData);
			}
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.KeyDown"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.KeyEventArgs"/> that contains the event data.</param>
		protected override void OnKeyDown(KeyEventArgs e)
		{
			keyDownHandled = e.Handled;

			if (this.KeyDown != null)
				this.KeyDown(this, e);

			if (e.Handled == false)
			{
				foreach (KeyboardAction keyboardAction in this.TextEditor.KeyboardActions)
				{
					if (this.ReadOnly == false || keyboardAction.AllowReadOnly == true)
					{
						if (keyboardAction.Key == e.KeyCode && keyboardAction.Alt == e.Alt && keyboardAction.Shift == e.Shift
							&& keyboardAction.Control == e.Control)
						{
							keyboardAction.Action();
						}
					}
				}

				switch (e.KeyCode)
				{
					case Keys.ShiftKey:
					case Keys.ControlKey:
					case Keys.Alt:
						break;
					case Keys.Down:
						if (e.Control == true)
							this.ScrollScreen(1);
						else
						{
							if (codeCompletionWindow.Visible == true)
								codeCompletionWindow.UpdateInput((char)Keys.Down, e.Control, e.Alt, e.Shift);
							else
								this.Caret.MoveDown(e.Shift);
						}

						this.Redraw();
						break;
					case Keys.Up:
						if (e.Control == true)
							this.ScrollScreen(-1);
						else
						{
							if (codeCompletionWindow.Visible == true)
								codeCompletionWindow.UpdateInput((char)Keys.Up, e.Control, e.Alt, e.Shift);
							else
								this.Caret.MoveUp(e.Shift);
						}

						this.Redraw();
						break;
					case Keys.Left:
						if (e.Control == true)
							this.MoveCaretToPreviousWord(e.Shift);
						else
							this.Caret.MoveLeft(e.Shift);

						this.Redraw();
						break;
					case Keys.Right:
						if (e.Control == true)
							this.MoveCaretToNextWord(e.Shift);
						else
							this.Caret.MoveRight(e.Shift);

						this.Redraw();
						break;
					case Keys.End:
						if (e.Control == true)
							this.Caret.MoveAbsoluteEnd(e.Shift);
						else
							this.Caret.MoveEnd(e.Shift);

						this.Redraw();
						break;
					case Keys.Home:
						if (e.Control == true)
							this.Caret.MoveAbsoluteHome(e.Shift);
						else
							this.Caret.MoveHome(e.Shift);

						this.Redraw();
						break;
					case Keys.PageDown:
						this.Caret.MoveDown(this.ViewPoint.VisibleRowCount - 2, e.Shift);

						this.Redraw();
						break;
					case Keys.PageUp:
						this.Caret.MoveUp(this.ViewPoint.VisibleRowCount - 2, e.Shift);

						this.Redraw();
						break;
					default:
						break;
				}

				if (this.ReadOnly == false)
				{
					switch (e.KeyCode)
					{
						case Keys.Enter:
							if (e.Control == true)
							{
								if (this.Caret.CurrentRow.CanFold == true)
								{
									this.Caret.MoveHome(false);
									this.Document.ToggleRowExpansion(Caret.CurrentRow);
									this.Redraw();
								}
							}
							else
							{
								if (codeCompletionWindow.Visible == true)
									codeCompletionWindow.UpdateInput((char)Keys.Enter, e.Control, e.Alt, e.Shift);
								else
									this.InsertEnter();
							}

							break;
						case Keys.Back:
							if (e.Control == false)
							{
								this.DeleteBackwards();
								codeCompletionWindow.UpdateInput((char)Keys.Back, e.Control, e.Alt, e.Shift);
							}
							else
							{
								if (this.Selection.IsValid == true)
									this.Selection.DeleteSelection();
								else
								{
									this.Selection.ClearSelection();
									this.MoveCaretToPreviousWord(true);
									this.Selection.DeleteSelection();
								}

								this.Caret.CurrentRow.Parse(true);
							}

							break;
						case Keys.Delete:
							if (e.Control == false && e.Alt == false && e.Shift == false)
							{
								this.Delete();
								codeCompletionWindow.UpdateInput((char)Keys.Delete, e.Control, e.Alt, e.Shift);
							}
							else if (e.Control == true && e.Alt == false && e.Shift == false)
							{
								if (this.Selection.IsValid == true)
									this.Cut();
								else
								{
									this.Selection.ClearSelection();
									this.MoveCaretToNextWord(true);
									this.Selection.DeleteSelection();
								}

								this.Caret.CurrentRow.Parse(true);
							}

							break;
						case Keys.Insert:
							if (e.Control == false && e.Alt == false && e.Shift == false)
								overWrite = overWrite == false;

							break;
						case Keys.Tab:
							if (this.Selection.IsValid == false)
							{
								if (keepTabs == false)
								{
									for (int i = 0; i < tabSpaces; i++)
										this.InsertText(" ");
								}
								else
									this.InsertText("\t");

								codeCompletionWindow.UpdateInput((char)Keys.Tab, e.Control, e.Alt, e.Shift);
							}
							
							break;
						default:
							break;
					}
				}

				this.Caret.Blink = true;
			}

			base.OnKeyDown(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.KeyPress"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.KeyPressEventArgs"/> that contains the event data.</param>
		protected override void OnKeyPress(KeyPressEventArgs e)
		{
			base.OnKeyPress(e);

			if (e.Handled == false && keyDownHandled == false && e.KeyChar != (char)127)
			{
				if (((int)e.KeyChar) < 32)
					return;

				if (this.ReadOnly == false)
				{
					this.InsertText(e.KeyChar.ToString());

					bool shift = e.KeyChar == '(' || e.KeyChar == ')';
					codeCompletionWindow.UpdateInput(e.KeyChar, false, false, shift);
					if (this.Indent == IndentStyle.Scope)
					{
						if (this.Caret.CurrentRow.ShouldOutdent == true)
							this.OutdentEndRow();
					}
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseDown"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
		protected override void OnMouseDown(MouseEventArgs e)
		{
			mouseX = e.X;
			mouseY = e.Y;
			mouseButton = e.Button;

			this.Focus();

			Row row = null;
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(e.X, e.Y);
			if (textPoint.Y >= 0 && textPoint.Y < this.Document.Count)
				row = this.Document[textPoint.Y];

			RowMouseEventArgs rowMouseEventArgs = new RowMouseEventArgs();
			rowMouseEventArgs.Row    = row;
			rowMouseEventArgs.Button = e.Button;
			rowMouseEventArgs.MouseX = mouseX;
			rowMouseEventArgs.MouseY = mouseY;

			if (e.X >= this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.TextArea;
			else if (e.X < this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.GutterArea;
			else if (e.X < this.ViewPoint.LineNumberMarginWidth + this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.LineNumberArea;
			else if (e.X < this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.FoldingArea;
			else
				rowMouseEventArgs.Area = RowArea.Other;

			this.OnRowMouseDown(rowMouseEventArgs);

			row = this.Document[textPoint.Y];
			if (row != null)
			{
				if (e.X >= row.ExpansionPixelEnd && row.IsCollapsed == true)
				{
					if (row.ExpansionStartSegment != null)
					{
						if (row.ExpansionStartSegment.StartRow != null && row.ExpansionStartSegment.EndRow != null && 
							row.ExpansionStartSegment.Expanded == false)
						{
							if (this.IsOverSelection(e.X, e.Y) == false)
							{
								this.Caret.Position.X = textPoint.X;
								this.Caret.Position.Y = textPoint.Y;
								this.Selection.ClearSelection();

								Row expansionEndRow = row.ExpansionEndRow;
								int expansionStartCharIndex = expansionEndRow.ExpansionStartChar;

								this.Caret.Position.X = expansionStartCharIndex;
								this.Caret.Position.Y = expansionEndRow.Index;
								this.Selection.MakeSelection();

								this.Redraw();
								this.ViewPoint.Action = TextAction.Select;

								return;
							}
						}
					}
				}
			}

			bool shift = NativeUser32Api.IsKeyPressed(Keys.ShiftKey);

			if (e.X > this.ViewPoint.TotalMarginWidth)
			{
				if (e.X > this.ViewPoint.TextMargin - 8)
				{
					if (this.IsOverSelection(e.X, e.Y) == false)
					{
						if (e.Button == MouseButtons.Left)
						{
							if (shift == false)
							{
								Word textPointWord = this.Document.GetWordFromPos(textPoint);
								if (textPointWord != null && textPointWord.Pattern != null && textPointWord.Pattern.Category != "")
								{
									WordMouseEventArgs wordMouseEventArgs = new WordMouseEventArgs();
									wordMouseEventArgs.Pattern = textPointWord.Pattern;
									wordMouseEventArgs.Button  = e.Button;
									wordMouseEventArgs.Word    = textPointWord;

									this.TextEditor.OnWordMouseDown(ref wordMouseEventArgs);
								}

								this.ViewPoint.Action = TextAction.Select;
								this.Caret.SetPosition(textPoint);
								this.Selection.ClearSelection();

								this.Caret.Blink = true;
								this.Redraw();
							}
							else
							{
								this.ViewPoint.Action = TextAction.Select;
								this.Caret.SetPosition(textPoint);
								this.Selection.MakeSelection();

								this.Caret.Blink = true;
								this.Redraw();
							}
						}
					}
				}
				else
				{
					if (row.ExpansionStartSegment != null)
					{
						this.Caret.SetPosition(new TextPoint(0, textPoint.Y));
						this.Selection.ClearSelection();

						this.Document.ToggleRowExpansion(row);
						this.Redraw();
					}
				}
			}
			else
			{
				if (e.X < this.ViewPoint.GutterMarginWidth)
				{
					if (this.TextEditor.AllowBreakPoints == true)
					{
						row = this.Document[this.Painter.GetTextPointAtPixel(e.X, e.Y).Y];
						row.Breakpoint = row.Breakpoint == false;
						this.Redraw();
					}
					else
					{
						row = this.Document[this.Painter.GetTextPointAtPixel(e.X, e.Y).Y];
						row.Breakpoint = false;
						this.Redraw();
					}
				}
				else
				{
					this.ViewPoint.Action = TextAction.Select;
					this.Caret.SetPosition(this.Painter.GetTextPointAtPixel(0, e.Y));
					this.Selection.ClearSelection();

					this.Caret.MoveDown(true);
					this.Redraw();
				}
			}

			this.SetMouseCursor(e.X, e.Y);
			base.OnMouseDown(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseMove"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
		protected override void OnMouseMove(MouseEventArgs e)
		{
			mouseX = e.X;
			mouseY = e.Y;
			mouseButton = e.Button;

			Row row = null;
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(e.X, e.Y);
			if (textPoint.Y >= 0 && textPoint.Y < Document.Count)
				row = Document[textPoint.Y];

			RowMouseEventArgs rowMouseEventArgs = new RowMouseEventArgs();
			rowMouseEventArgs.Row    = row;
			rowMouseEventArgs.Button = e.Button;
			rowMouseEventArgs.MouseX = mouseX;
			rowMouseEventArgs.MouseY = mouseY;

			if (e.X >= this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.TextArea;
			else if (e.X < this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.GutterArea;
			else if (e.X < this.ViewPoint.LineNumberMarginWidth + this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.LineNumberArea;
			else if (e.X < this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.FoldingArea;
			else
				rowMouseEventArgs.Area = RowArea.Other;

			this.OnRowMouseMove(rowMouseEventArgs);

			if (this.Document != null)
			{
				if (e.Button == MouseButtons.Left)
				{
					if (this.ViewPoint.Action == TextAction.Select)
					{
						this.Caret.Blink = true;
						this.Caret.SetPosition(textPoint);
						if (e.X <= ViewPoint.TotalMarginWidth)
							this.Caret.MoveDown(true);

						this.Caret.CropPosition();
						this.Selection.MakeSelection();
						this.ScrollIntoView();
						this.Redraw();
					}
					else if (this.ViewPoint.Action == TextAction.None)
					{
						if (this.IsOverSelection(e.X, e.Y) == true)
							this.BeginDragDrop();
					}
				}
				else
				{
					row = this.Document[textPoint.Y];
					bool showTooltip = false;

					if (row != null)
					{
						if (e.X < this.ViewPoint.TextMargin - 8 && e.X > this.ViewPoint.TotalMarginWidth)
						{
							bool shouldContinue = true;

							int startRowIndex = textPoint.Y;
							Row endRow = row.ExpansionEndRow;

							if (row.CanFold == false)
							{
								int index = row.StartSegment != null && row.StartSegment.StartRow != null ? this.Document.IndexOf(row.StartSegment.StartRow) : -1;

								if (index < 0)
									shouldContinue = false;

								startRowIndex = index;
								endRow = row.StartSegment != null ? row.StartSegment.EndRow : null;
							}

							if (shouldContinue == true)
							{
								if (this.Document[startRowIndex].Expanded == true)
								{
									for (int rowIndex = 0; rowIndex < this.Document.Count; rowIndex++)
									{
										this.Document[rowIndex].BackColor = Color.Transparent;
										this.Document[rowIndex].Hovered = false;
									}

									for (int rowIndex = startRowIndex; rowIndex <= this.Document.IndexOf(endRow); rowIndex++)
									{
										this.Document[rowIndex].BackColor = this.RowHoverBackColor;
										this.Document[rowIndex].Hovered = true;
									}
								}

								this.Redraw();
							}
						}
						else
						{
							for (int rowIndex = 0; rowIndex < this.Document.Count; rowIndex++)
							{
								this.Document[rowIndex].BackColor = Color.Transparent;
								this.Document[rowIndex].Hovered = false;
							}

							this.Redraw();
						}

						if (e.X >= row.ExpansionPixelEnd && row.IsCollapsed)
						{
							if (row.ExpansionStartSegment != null)
							{
								if (row.ExpansionStartSegment.StartRow != null && row.ExpansionStartSegment.EndRow != null &&
									row.ExpansionStartSegment.Expanded == false)
								{
									string tabbedText = "";
									for (int rowIndex = this.Document.IndexOf(row.ExpansionStartSegment.StartRow); rowIndex <= 
										row.ExpansionStartSegment.EndRow.Index; rowIndex++)
									{
										if (rowIndex > 0)
											tabbedText += "\n";

										Row currentRow = this.Document[rowIndex];
										string removedTabsText = currentRow.Text.Replace("\t", "    ");
										tabbedText += removedTabsText;
										if (rowIndex > 20)
										{
											tabbedText += "...";
											break;
										}

										rowIndex++;
									}

									tooltip.InitialDelay = TooltipDelay;
									if (tooltip.GetToolTip(this) != tabbedText)
										tooltip.SetToolTip(this, tabbedText);

									tooltip.Active = true;
									showTooltip = true;
								}
							}
						}
					}

					if (tooltip != null)
					{
						if (showTooltip == false)
							tooltip.SetToolTip(this, "");
					}
				}

				this.SetMouseCursor(e.X, e.Y);
				base.OnMouseMove(e);
			}
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseUp"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
		protected override void OnMouseUp(MouseEventArgs e)
		{
			mouseX = e.X;
			mouseY = e.Y;
			mouseButton = e.Button;

			Row row = null;
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(e.X, e.Y);
			if (textPoint.Y >= 0 && textPoint.Y < Document.Count)
				row = this.Document[textPoint.Y];

			RowMouseEventArgs rowMouseEventArgs = new RowMouseEventArgs();
			rowMouseEventArgs.Row    = row;
			rowMouseEventArgs.Button = e.Button;
			rowMouseEventArgs.MouseX = mouseX;
			rowMouseEventArgs.MouseY = mouseY;

			if (e.X >= this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.TextArea;
			else if (e.X < this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.GutterArea;
			else if (e.X < this.ViewPoint.LineNumberMarginWidth + this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.LineNumberArea;
			else if (e.X < this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.FoldingArea;
			else
				rowMouseEventArgs.Area = RowArea.Other;

			this.OnRowMouseUp(rowMouseEventArgs);

			if (this.ViewPoint.Action == TextAction.None)
			{
				if (e.X > ViewPoint.TotalMarginWidth)
				{
					if (this.IsOverSelection(e.X, e.Y) == true && e.Button == MouseButtons.Left)
					{
						this.ViewPoint.Action = TextAction.Select;
						this.Caret.SetPosition(this.Painter.GetTextPointAtPixel(e.X, e.Y));

						this.Selection.ClearSelection();
						this.Redraw();
					}
				}
			}

			this.ViewPoint.Action = TextAction.None;
			base.OnMouseUp(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseWheel"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
		protected override void OnMouseWheel(MouseEventArgs e)
		{
			int scrollLines = SystemInformation.MouseWheelScrollLines;
			this.ScrollScreen(-(e.Delta / 120) * scrollLines, 2);

			base.OnMouseWheel(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.Paint"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data.</param>
		protected override void OnPaint(PaintEventArgs e)
		{
			if (this.Document != null && Width > 0 && Height > 0)
				this.Painter.RenderAll(e.Graphics);
		}

		/// <summary>
		/// Raises the <see cref="E:Resize"/> event.
		/// </summary>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		protected override void OnResize(EventArgs e)
		{
			this.UpdateContent();
			base.OnResize(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.DragOver"/> event.
		/// </summary>
		/// <param name="drgevent">A <see cref="T:System.Windows.Forms.DragEventArgs"/> that contains the event data.</param>
		protected override void OnDragOver(DragEventArgs drgevent)
		{
			if (this.ReadOnly == false)
			{
				if (this.Document != null)
				{
					this.ViewPoint.Action = TextAction.DragText;

					Point point = this.PointToClient(new Point(drgevent.X, drgevent.Y));

					int x = point.X;
					int y = point.Y;

					if ((drgevent.KeyState & 8) == 8)
						drgevent.Effect = DragDropEffects.Copy;
					else
						drgevent.Effect = DragDropEffects.Move;

					this.Caret.SetPosition(this.Painter.GetTextPointAtPixel(x, y));
					this.Redraw();
				}
			}
			else
				drgevent.Effect = DragDropEffects.None;

			base.OnDragOver(drgevent);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.DragDrop"/> event.
		/// </summary>
		/// <param name="drgevent">A <see cref="T:System.Windows.Forms.DragEventArgs"/> that contains the event data.</param>
		protected override void OnDragDrop(DragEventArgs drgevent)
		{
			if (this.ReadOnly == false)
			{
				if (this.Document != null)
				{
					this.ViewPoint.Action = TextAction.None;

					int selectionStart = this.Selection.LogicalSelStart;
					int dropPosition = this.Document.PointToIntPos(this.Caret.Position);

					string dropText = drgevent.Data.GetData(typeof(string)).ToString();
					int dropTextLength = dropText.Length;

					if (dropPosition >= selectionStart && dropPosition <= selectionStart + Math.Abs(this.Selection.SelLength))
						dropPosition = selectionStart;
					else if (dropPosition >= selectionStart + dropTextLength)
						dropPosition -= dropTextLength;

					this.Document.StartUndoCapture();
					if ((drgevent.KeyState & 8) == 0)
					{
						this.TextEditor.Selection.DeleteSelection();
						this.Caret.Position = this.Document.IntPosToPoint(dropPosition);
					}

					TextPoint textPoint = this.Document.InsertText(dropText, Caret.Position.X, Caret.Position.Y);
					this.Document.EndUndoCapture();

					this.Selection.SelStart  = this.Document.PointToIntPos(this.Caret.Position);
					this.Selection.SelLength = dropTextLength;

					this.Document.ResetVisibleRows();
					this.ScrollIntoView();
					this.Redraw();

					drgevent.Effect = DragDropEffects.All;

					if (this.ParseOnPaste == true)
						this.Document.ParseAll(true);

					this.ViewPoint.Action = TextAction.None;
				}
			}

			base.OnDragDrop(drgevent);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.DragLeave"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
		protected override void OnDragLeave(EventArgs e)
		{
			this.ViewPoint.Action = TextAction.None;
			base.OnDragLeave(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.DoubleClick"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
		protected override void OnDoubleClick(EventArgs e)
		{
			Row row = null;
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(mouseX, mouseY);
			if (textPoint.Y >= 0 && textPoint.Y < Document.Count)
				row = this.Document[textPoint.Y];

			RowMouseEventArgs rowMouseEventArgs = new RowMouseEventArgs();
			rowMouseEventArgs.Row    = row;
			rowMouseEventArgs.Button = MouseButtons.None;
			rowMouseEventArgs.MouseX = mouseX;
			rowMouseEventArgs.MouseY = mouseY;

			if (mouseX >= this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.TextArea;
			else if (mouseX < this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.GutterArea;
			else if (mouseX < this.ViewPoint.LineNumberMarginWidth + ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.LineNumberArea;
			else if (mouseX < this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.FoldingArea;
			else
				rowMouseEventArgs.Area = RowArea.Other;

			this.OnRowDoubleClick(rowMouseEventArgs);

			row = this.Document[textPoint.Y];
			if (row != null)
			{
				if (mouseX >= row.ExpansionPixelEnd && row.IsCollapsed == true)
				{
					if (row.ExpansionStartSegment != null)
					{
						if (row.ExpansionStartSegment.StartRow != null && row.ExpansionStartSegment.EndRow != null &&
							row.ExpansionStartSegment.Expanded == false)
						{
							row.Expanded = true;

							this.Document.ResetVisibleRows();
							this.Redraw();

							return;
						}
					}
				}
			}

			if (mouseX > this.ViewPoint.TotalMarginWidth)
				this.SelectCurrentWord();
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.Click"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
		protected override void OnClick(EventArgs e)
		{
			Row row = null;
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(mouseX, mouseY);
			if (textPoint.Y >= 0 && textPoint.Y < this.Document.Count)
				row = this.Document[textPoint.Y];

			RowMouseEventArgs rowMouseEventArgs = new RowMouseEventArgs();
			rowMouseEventArgs.Row    = row;
			rowMouseEventArgs.Button = MouseButtons.None;
			rowMouseEventArgs.MouseX = mouseX;
			rowMouseEventArgs.MouseY = mouseY;

			if (mouseX >= this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.TextArea;
			else if (mouseX < this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.GutterArea;
			else if (mouseX < this.ViewPoint.LineNumberMarginWidth + this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.LineNumberArea;
			else if (mouseX < this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.FoldingArea;
			else
				rowMouseEventArgs.Area = RowArea.Other;

			this.OnRowClick(rowMouseEventArgs);
		}

		/// <summary>
		/// Called when [vertical scroll bar scroll].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.ScrollEventArgs"/> instance containing the event data.</param>
		private void OnVerticalScrollBarScroll(object sender, System.Windows.Forms.ScrollEventArgs e)
		{
			this.SetMaxHorizontalScroll();
			this.Focus();

			int difference = e.NewValue - vScroll.Value;
			if ((difference == -1 || difference == 1) && (e.Type == ScrollEventType.SmallDecrement || e.Type == ScrollEventType.SmallIncrement))
				this.ScrollScreen(difference);
			else
				this.Invalidate();
		}

		/// <summary>
		/// Called when [horitonzal scroll bar scroll].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.ScrollEventArgs"/> instance containing the event data.</param>
		private void OnHoritonzalScrollBarScroll(object sender, System.Windows.Forms.ScrollEventArgs e)
		{
			this.Focus();
			this.Invalidate();
		}

		/// <summary>
		/// Called when [caret timer tick].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnCaretTimerTick(object sender, EventArgs e)
		{
			this.Caret.Blink = this.Caret.Blink == false;
			this.RedrawCaret();
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseLeave"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
		protected override void OnMouseLeave(EventArgs e)
		{
			base.OnMouseLeave(e);
			if (tooltip != null)
				tooltip.RemoveAll();
		}

		/// <summary>
		/// Called when [caret changed].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnCaretChanged(object sender, EventArgs e)
		{
			this.OnCaretChange();
		}

		/// <summary>
		/// Called when [leave].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnLeave(object sender, EventArgs e)
		{
			this.RemoveFocus();
		}

		/// <summary>
		/// Called when [enter].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnEnter(object sender, EventArgs e)
		{
			caretTimer.Enabled = true;
		}

		/// <summary>
		/// Called when [selection changed].
		/// </summary>
		/// <param name="s">The s.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnSelectionChanged(object s, EventArgs e)
		{
			this.OnSelectionChange();
		}

		/// <summary>
		/// Called when [caret change].
		/// </summary>
		private void OnCaretChange()
		{
			if (this.CaretChange != null)
				this.CaretChange(this, null);
		}

		/// <summary>
		/// Called when [selection change].
		/// </summary>
		private void OnSelectionChange()
		{
			if (this.SelectionChange != null)
				this.SelectionChange(this, null);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.VisibleChanged"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
		protected override void OnVisibleChanged(EventArgs e)
		{
			if (this.Visible == false)
				this.RemoveFocus();

			base.OnVisibleChanged(e);
			this.UpdateContent();
		}

		#endregion

		#endregion

		/// <summary>
		/// Initializes a new instance of TextEditorBase.
		/// </summary>
		public TextEditorBase(TextEditor parent)
			: base(parent)
		{
			painter   = new DefaultPainter(this);
			selection = new Selection(this);
			caret     = new Caret(this);

			caret.Change     += this.OnCaretChanged;
			selection.Change += this.OnSelectionChanged;

			this.InitializeComponent();

			this.SetStyle(ControlStyles.AllPaintingInWmPaint, false);
			this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
			this.SetStyle(ControlStyles.Selectable, true);
			this.SetStyle(ControlStyles.ResizeRedraw, true);
			this.SetStyle(ControlStyles.Opaque, true);
			this.SetStyle(ControlStyles.UserPaint, true);
		}
	}
}
